package com.vehicle.common.security.aspect;

import java.lang.reflect.Method;
import java.util.Collection;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;
import com.vehicle.common.core.exception.PreAuthorizeException;
import com.vehicle.common.core.utils.StringUtils;
import com.vehicle.common.security.annotation.PreAuthorize;
import com.vehicle.common.security.service.TokenService;
import com.vehicle.system.api.model.LoginUser;

/**
 * 自定义权限实现
 *
 * @author vehicle
 */
// 声明这是一个 AOP 切面类，Spring 会自动识别并处理
@Aspect
// 声明这是一个 Spring 组件，会被 Spring 容器管理
@Component
public class PreAuthorizeAspect
{
    // 自动注入 TokenService 实例，用于获取登录用户信息
    @Autowired
    private TokenService tokenService;

    /** 所有权限标识，当用户拥有此权限时，表示拥有所有权限 */
    private static final String ALL_PERMISSION = "*:*:*";

    /** 管理员角色权限标识，拥有该角色的用户拥有最高权限 */
    private static final String SUPER_ADMIN = "admin";

    /** 数组为 0 时的常量，用于判断数组是否为空 */
    private static final Integer ARRAY_EMPTY = 0;

    /**
     * 环绕通知，用于处理带有 @PreAuthorize 注解的方法
     * 在方法执行前后进行权限验证
     * @param point 连接点，包含了被代理方法的信息
     * @return 方法执行的结果
     * @throws Throwable 可能抛出的异常
     */
    @Around("@annotation(com.vehicle.common.security.annotation.PreAuthorize)")
    public Object around(ProceedingJoinPoint point) throws Throwable
    {
        // 获取被代理方法的签名信息
        Signature signature = point.getSignature();
        // 将签名信息转换为方法签名，方便获取方法相关信息
        MethodSignature methodSignature = (MethodSignature) signature;
        // 获取被代理的方法对象
        Method method = methodSignature.getMethod();
        // 获取方法上的 @PreAuthorize 注解实例
        PreAuthorize annotation = method.getAnnotation(PreAuthorize.class);
        // 如果方法上没有 @PreAuthorize 注解，直接执行方法
        if (annotation == null)
        {
            return point.proceed();
        }

        // 如果注解中设置了 hasPermi 属性，验证用户是否具备该权限
        if (StringUtils.isNotEmpty(annotation.hasPermi()))
        {
            if (hasPermi(annotation.hasPermi()))
            {
                // 用户具备该权限，执行方法
                return point.proceed();
            }
            // 用户不具备该权限，抛出权限验证异常
            throw new PreAuthorizeException();
        }
        // 如果注解中设置了 lacksPermi 属性，验证用户是否不具备该权限
        else if (StringUtils.isNotEmpty(annotation.lacksPermi()))
        {
            if (lacksPermi(annotation.lacksPermi()))
            {
                // 用户不具备该权限，执行方法
                return point.proceed();
            }
            // 用户具备该权限，抛出权限验证异常
            throw new PreAuthorizeException();
        }
        // 如果注解中设置了 hasAnyPermi 属性，验证用户是否具备任意一个权限
        else if (ARRAY_EMPTY < annotation.hasAnyPermi().length)
        {
            if (hasAnyPermi(annotation.hasAnyPermi()))
            {
                // 用户具备任意一个权限，执行方法
                return point.proceed();
            }
            // 用户不具备任意一个权限，抛出权限验证异常
            throw new PreAuthorizeException();
        }
        // 如果注解中设置了 hasRole 属性，验证用户是否具备该角色
        else if (StringUtils.isNotEmpty(annotation.hasRole()))
        {
            if (hasRole(annotation.hasRole()))
            {
                // 用户具备该角色，执行方法
                return point.proceed();
            }
            // 用户不具备该角色，抛出权限验证异常
            throw new PreAuthorizeException();
        }
        // 如果注解中设置了 lacksRole 属性，验证用户是否不具备该角色
        else if (StringUtils.isNotEmpty(annotation.lacksRole()))
        {
            if (lacksRole(annotation.lacksRole()))
            {
                // 用户不具备该角色，执行方法
                return point.proceed();
            }
            // 用户具备该角色，抛出权限验证异常
            throw new PreAuthorizeException();
        }
        // 如果注解中设置了 hasAnyRoles 属性，验证用户是否具备任意一个角色
        else if (ARRAY_EMPTY < annotation.hasAnyRoles().length)
        {
            if (hasAnyRoles(annotation.hasAnyRoles()))
            {
                // 用户具备任意一个角色，执行方法
                return point.proceed();
            }
            // 用户不具备任意一个角色，抛出权限验证异常
            throw new PreAuthorizeException();
        }

        // 如果以上条件都不满足，直接执行方法
        return point.proceed();
    }

    /**
     * 验证用户是否具备某权限
     *
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    public boolean hasPermi(String permission)
    {
        // 从 TokenService 中获取当前登录用户信息
        LoginUser userInfo = tokenService.getLoginUser();
        // 如果用户信息为空或者用户的权限列表为空，返回 false
        if (StringUtils.isNull(userInfo) || CollectionUtils.isEmpty(userInfo.getPermissions()))
        {
            return false;
        }
        // 调用 hasPermissions 方法，判断用户是否具备该权限
        return hasPermissions(userInfo.getPermissions(), permission);
    }

    /**
     * 验证用户是否不具备某权限，与 hasPermi 逻辑相反
     *
     * @param permission 权限字符串
     * @return 用户是否不具备某权限
     */
    public boolean lacksPermi(String permission)
    {
        // 调用 hasPermi 方法，取反结果
        return hasPermi(permission) != true;
    }

    /**
     * 验证用户是否具有以下任意一个权限
     *
     * @param permissions 权限列表
     * @return 用户是否具有以下任意一个权限
     */
    public boolean hasAnyPermi(String[] permissions)
    {
        // 从 TokenService 中获取当前登录用户信息
        LoginUser userInfo = tokenService.getLoginUser();
        // 如果用户信息为空或者用户的权限列表为空，返回 false
        if (StringUtils.isNull(userInfo) || CollectionUtils.isEmpty(userInfo.getPermissions()))
        {
            return false;
        }
        // 获取用户的权限列表
        Collection<String> authorities = userInfo.getPermissions();
        // 遍历权限列表
        for (String permission : permissions)
        {
            // 如果权限不为空且用户具备该权限，返回 true
            if (permission != null && hasPermissions(authorities, permission))
            {
                return true;
            }
        }
        // 用户不具备任意一个权限，返回 false
        return false;
    }

    /**
     * 判断用户是否拥有某个角色
     *
     * @param role 角色字符串
     * @return 用户是否具备某角色
     */
    public boolean hasRole(String role)
    {
        // 从 TokenService 中获取当前登录用户信息
        LoginUser userInfo = tokenService.getLoginUser();
        // 如果用户信息为空或者用户的角色列表为空，返回 false
        if (StringUtils.isNull(userInfo) || CollectionUtils.isEmpty(userInfo.getRoles()))
        {
            return false;
        }
        // 遍历用户的角色列表
        for (String roleKey : userInfo.getRoles())
        {
            // 如果用户是管理员角色或者具备该角色，返回 true
            if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(role))
            {
                return true;
            }
        }
        // 用户不具备该角色，返回 false
        return false;
    }

    /**
     * 验证用户是否不具备某角色，与 isRole 逻辑相反。
     *
     * @param role 角色名称
     * @return 用户是否不具备某角色
     */
    public boolean lacksRole(String role)
    {
        // 调用 hasRole 方法，取反结果
        return hasRole(role) != true;
    }

    /**
     * 验证用户是否具有以下任意一个角色
     *
     * @param roles 角色列表
     * @return 用户是否具有以下任意一个角色
     */
    public boolean hasAnyRoles(String[] roles)
    {
        // 从 TokenService 中获取当前登录用户信息
        LoginUser userInfo = tokenService.getLoginUser();
        // 如果用户信息为空或者用户的角色列表为空，返回 false
        if (StringUtils.isNull(userInfo) || CollectionUtils.isEmpty(userInfo.getRoles()))
        {
            return false;
        }
        // 遍历角色列表
        for (String role : roles)
        {
            // 如果用户具备该角色，返回 true
            if (hasRole(role))
            {
                return true;
            }
        }
        // 用户不具备任意一个角色，返回 false
        return false;
    }

    /**
     * 判断是否包含权限
     *
     * @param authorities 权限列表
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    private boolean hasPermissions(Collection<String> authorities, String permission)
    {
        // 使用流处理权限列表，过滤掉空字符串
        // 然后判断是否有任意一个权限满足条件：是所有权限标识或者能匹配该权限字符串
        return authorities.stream().filter(StringUtils::hasText)
                .anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission));
    }
}